/*
 * $Id: Page.java 42 2013-04-17 15:33:59Z gabakyan $
 * $Author: gabakyan $
 * $Revision: 42 $
 * $Date: 2013-04-17 15:33:59 +0000 (Wed, 17 Apr 2013) $
 *
 * Copyright (c) 2013 Supply Chain Intelligence (SCI), Inc.
 * http://www.scintelligence.com/, Email: info@scintelligence.com
 * All rights reserved.
 *
 * This file is part of Logistics Map.
 *
 * Logistics Map is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * Logistics Map is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Logistics Map.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.sci.logisticsmap.support;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * A page is a sublist of a list of objects. It allows gain information about
 * the position of it in the containing entire list.
 *
 * @param <T> the type of which the page consists.
 */
public class Page<T> implements Iterable<T> {
    /**
     * The content.
     */
    private final List<T> content = new ArrayList<T>();
    /**
     * The number.
     */
    private final int number;
    /**
     * The size.
     */
    private final int size;
    /**
     * The total.
     */
    private final long total;

    /**
     * Constructor of {@code Page}.
     *
     * @param content the content of this page
     * @param number the page number
     * @param size the page size
     * @param total the total amount of items available
     */
    public Page(List<T> content, int number, int size, long total) {
        this.content.addAll(content);
        this.number = number;
        this.size = size;
        this.total = total;
    }

    /**
     * Gets the number.
     *
     * @return The number.
     */
    public int getNumber() {
        return number;
    }

    /**
     * Gets the size.
     *
     * @return The size.
     */
    public int getSize() {
        return size;
    }

    /**
     * Gets the total pages.
     *
     * @return The total pages.
     */
    public int getTotalPages() {
        return getSize() == 0 ? 0 : (int) Math.ceil((double) total / (double) getSize());
    }

    /**
     * Gets the content size.
     *
     * @return The content size.
     */
    public int getContentSize() {
        return content.size();
    }

    /**
     * Gets the total.
     *
     * @return The total.
     */
    public long getTotal() {
        return total;
    }

    /**
     * Checks has the previous page.
     *
     * @return <code>true</code> if has the previous page, otherwise
     * <code>false</code>.
     */
    public boolean hasPreviousPage() {
        return getNumber() > 1;
    }

    /**
     * Checks is the first page.
     *
     * @return <code>true</code> if the first page, otherwise
     * <code>false</code>.
     */
    public boolean isFirstPage() {
        return !hasPreviousPage();
    }

    /**
     * Checks has the next page.
     *
     * @return <code>true</code> if has the next page, otherwise
     * <code>false</code>.
     */
    public boolean hasNextPage() {
        return ((getNumber() + 0) * getSize()) < total;
    }

    /**
     * Checks is the last page.
     *
     * @return <code>true</code> if the last page, otherwise
     * <code>false</code>.
     */
    public boolean isLastPage() {
        return !hasNextPage();
    }

	/*
     * (non-Javadoc)
     *
     * @see java.lang.Iterable#iterator()
     */
    public Iterator<T> iterator() {
        return content.iterator();
    }

    /**
     * Gets the unmodifiable content.
     *
     * @return The unmodifiable content.
     */
    public List<T> getContent() {
        return Collections.unmodifiableList(content);
    }

    /**
     * Checks has the content.
     *
     * @return <code>true</code> if {@link #content} is empty, otherwise
     * <code>false</code>.
     */
    public boolean hasContent() {
    	return !content.isEmpty();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        String contentType = "UNKNOWN";

        if (content.size() > 0) {
            contentType = content.get(0).getClass().getName();
        }

        return String.format("Page %d of %d containing %s instances",
                getNumber(), getTotalPages(), contentType);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Page)) return false;

        Page page = (Page) o;

        if (number != page.number) return false;
        if (size != page.size) return false;
        if (total != page.total) return false;
        if (content != null ? !content.equals(page.content) : page.content != null) return false;

        return true;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        int result = content != null ? content.hashCode() : 0;
        result = 31 * result + number;
        result = 31 * result + size;
        result = 31 * result + (int) (total ^ (total >>> 32));
        return result;
    }
}
